#pragma once

#include "error.hpp"
#include <iostream>
#include <cstdlib>
#include <cstring>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

using namespace std;

const int gbacklog = 32;

struct Sock
{
    static int Socket()
    {
        int sock = socket(AF_INET, SOCK_STREAM, 0);
        if (sock == -1)
        {
            cout << "create socket error" << endl;
            exit(SOCK_ERR);
        }
        int opt = 1;
        //服务器先于客户端关闭连接 服务器进程在一段时间处于timewait状态 由于进程占用端口 所以会出现端口暂时无法bind的情况 下面这个接口会解决这一问题
        setsockopt(sock, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &opt, sizeof opt);
        return sock;
    }

    static void Bind(int sock, uint16_t port)
    {
        struct sockaddr_in server;
        //AF_INET AF_UNIX
        memset(&server, 0, sizeof server);
        server.sin_family = AF_INET;
        server.sin_addr.s_addr = INADDR_ANY;
        server.sin_port = htons(port);
        int n = bind(sock, (struct sockaddr*)&server, sizeof server);
        if (n == -1)
        {
            cout << "bind error" << endl;
            exit(BIND_ERR);
        }
        cout << "bind success, listenfd: " << sock << endl;
    }

    static void Listen(int sock)
    {
        int n = listen(sock, gbacklog);
        if (n == -1)
        {
            cout << "set listen error" << endl;
            exit(LISTEN_ERR);
        }
        cout << "set listen success" << endl;
    }

    static int Accept(int sock, struct sockaddr_in* out)
    {
        struct sockaddr_in client;
        socklen_t len = sizeof client;
        memset(&client, 0, sizeof client);
        int n = accept(sock, (struct sockaddr*)&client, &len);
        if (n == -1)
        {
            cout << "accept error" << endl;
            exit(ACCEPT_ERR);
        }
        *out = client;
        return n;
    }
};